use bytes::{Buf, BufMut, Bytes, BytesMut};
use tokio::io::Error;
use tokio_util::codec::{Decoder, Encoder};

use std::borrow::Borrow;
use std::io;
use std::io::ErrorKind;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct PacketBytesCodec(());

pub trait Transmission {
    fn get_cmd(&self) -> i16;
    fn get_serialization_type(&self) -> i8;
    fn get_body_length(&self) -> i32;
    fn get_body(&self) -> &Bytes;
    fn get_rpc_num(&self) -> i8;
    fn get_lrc(&self) -> i8;
    fn get_flags(&self) -> i8;
    fn set_rpc_num(&mut self, rpc_num: i8);
    fn set_cmd(&mut self, cmd: i16);
    fn set_flags(&mut self, flags: i8);
    fn set_serialization_type(&mut self, serialization_type: i8);
    fn set_lrc(&mut self, lrc: i8);
    fn set_body_length(&mut self, body_length: i32);
    fn set_body(&mut self, body: Bytes);
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Clone)]
pub struct Packet {
    rpc_num: i8,
    cmd: i16,
    flags: i8,
    serialization_type: i8,
    lrc: i8,
    body_length: i32,
    body: Bytes,
}

impl Transmission for Packet {
    fn get_cmd(&self) -> i16 {
        self.cmd
    }

    fn get_serialization_type(&self) -> i8 {
        self.serialization_type
    }

    fn get_body_length(&self) -> i32 {
        self.body_length
    }

    fn get_body(&self) -> &Bytes {
        &self.body
    }

    fn get_rpc_num(&self) -> i8 {
        self.rpc_num
    }
    fn get_lrc(&self) -> i8 {
        self.lrc
    }

    fn get_flags(&self) -> i8 {
        self.flags
    }

    fn set_rpc_num(&mut self, rpc_num: i8) {
        self.rpc_num = rpc_num;
    }
    fn set_cmd(&mut self, cmd: i16) {
        self.cmd = cmd;
    }
    fn set_flags(&mut self, flags: i8) {
        self.flags = flags;
    }
    fn set_serialization_type(&mut self, serialization_type: i8) {
        self.serialization_type = serialization_type;
    }
    fn set_lrc(&mut self, lrc: i8) {
        self.lrc = lrc;
    }
    fn set_body_length(&mut self, body_length: i32) {
        self.body_length = body_length;
    }
    fn set_body(&mut self, body: Bytes) {
        self.body = body;
    }
}

impl Packet {
    const HEAD_LENGTH: usize = 10;
    const LENGTH_FIELD_OFFSET: usize = 6;
    const LENGTH_FIELD_LENGTH: usize = 4;
    const PACKET_MAX_LENGTH: usize = 0x7fffffff;
}

#[derive(Debug)]
pub enum DCodecError<U: Decoder> {
    /// The maximum line length was exceeded.
    MaxLineLengthExceeded,
    /// An IO error occured.
    Io(U::Error),
}
impl Decoder for PacketBytesCodec {
    type Item = Packet;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
        if !buf.is_empty() {
            if buf.len() >= Packet::HEAD_LENGTH {
                let x: &[u8] = buf
                    .get(
                        Packet::LENGTH_FIELD_OFFSET
                            ..Packet::LENGTH_FIELD_OFFSET + Packet::LENGTH_FIELD_LENGTH,
                    )
                    .unwrap();
                let body_length = BytesMut::from(x).get_i32();
                if body_length as usize + Packet::HEAD_LENGTH < buf.len() {
                    return Ok(None);
                }
                let rpc_num = buf.get_i8();
                let cmd = buf.get_i16();
                let flags = buf.get_i8();
                let serialization_type = buf.get_i8();
                let lrc = buf.get_i8();
                let body_length = buf.get_i32();
                let body = buf.split_to(body_length as usize).freeze();

                return Ok(Some(Packet {
                    rpc_num,
                    cmd,
                    flags,
                    serialization_type,
                    lrc,
                    body_length,
                    body,
                }));
            }
            Ok(None)
        } else {
            Ok(None)
        }
    }
}

impl Encoder<Packet> for PacketBytesCodec {
    type Error = io::Error;

    fn encode(&mut self, data: Packet, buf: &mut BytesMut) -> Result<(), Self::Error> {
        //     rpc_num: i8,
        //     cmd: i16,
        //     flags:i8,
        //     serialization_type:i8,
        //     lrc:i8,
        //     body_length:i32,
        buf.reserve(Packet::HEAD_LENGTH + data.body_length as usize);
        buf.put_i8(data.rpc_num);
        buf.put_i16(data.cmd);
        buf.put_i8(data.flags);
        buf.put_i8(data.serialization_type);
        buf.put_i8(data.lrc);
        buf.put_i32(data.body_length);
        buf.put_slice(data.body.borrow());
        if buf.len() < Packet::HEAD_LENGTH {
            return Err(Error::from(ErrorKind::UnexpectedEof));
        }
        Ok(())
    }
}
